package lode

import (
	"archive/tar"
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/Unixeno/islands/conf"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync/atomic"
	"time"
)

func Commit(imageID, rwLayer, name string) (string, error) {
	logger.WithField("image", name).Info("start commit")
	logger.Debug("rwLayer path is ", rwLayer)

	tmpEntry, ok := lodeDB.images.syncDB.Load(imageID)
	if !ok {
		logger.Error("The image of the container not exists")
		return "", fmt.Errorf("The image of the container not exists")
	}
	imageEntry := tmpEntry.(*imageInfoEntry)
	layerId, err := lodeDB.layers.storeDir(rwLayer)
	if err != nil {
		logger.WithError(err).Error("an error occurred while store the container's rw-layer")
		return "", err
	}

	imageInfo := imageEntry.Info

	imageInfo.Created = time.Now()
	imageInfo.Rootfs = append(imageInfo.Rootfs, layerId)

	id, err := lodeDB.images.store(imageInfo, name)
	if err != nil {
		logger.WithError(err).Error("failed to store image")
		return "", err
	} else {
		logger.WithField("ID", id).Debug("commit success")
		return id, nil
	}
}

func (db layerDB) storeDir (src string) (string, error) {
	hash := sha256.New()
	var byteBuffer bytes.Buffer
	tw := tar.NewWriter(&byteBuffer)
	err := filepath.Walk(src, func(file string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		header, err := tar.FileInfoHeader(fi, fi.Name())
		header.Name = strings.TrimPrefix(strings.Replace(file, src,"" , -1), string(filepath.Separator))
		if err := tw.WriteHeader(header); err != nil {
			return err
		}
		if fi.Mode().IsRegular(){
			f, err := os.Open(file)
			if err != nil {
				return err
			}
			if _, err := io.Copy(tw, f); err != nil {
				return err
			}
			f.Close()
		}
		return nil
	})
	if err != nil {
		return "", err
	}
	err = tw.Flush()
	if err != nil {
		return "", err
	}
	size, err := io.Copy(hash, &byteBuffer)
	hashInBytes := hash.Sum(nil)
	id := hex.EncodeToString(hashInBytes)

	defer db.save()
	layerPath := filepath.Join(conf.GetString("RootPath"), "layerdb", id)
	if layer, ok := db.syncDB.LoadOrStore(id, &layerInfoEntry{
		Id:         id,
		Path:       layerPath,
		Size:       size,
		ImportTime: time.Now(),
		Counter:	1,
	}); ok {
		atomic.AddInt32(&layer.(*layerInfoEntry).Counter, 1)
		return id, nil
	} else {
		err = os.MkdirAll(filepath.Join(conf.GetString("RootPath"), "layerdb",id), 0700)
		if err != nil {
			logger.WithError(err).Error("an error occurred while creating layer directory")
			return "", err
		}
		err = CopyDir(src, layerPath)
		if err != nil {
			return "", err
		}

		return id, nil
	}
}


